Functors and monads
Functors and monads are abstractions for sequencing computations that carry some extra
structure — optionality (Maybe), multiplicity ([]), side effects (<Proc>), or
discrete-event timing (Sequence). SCL supports these abstractions through type classes
and dedicated block syntax.
Functor
A Functor is any type constructor f for which you can apply a function to the
contained value without changing the structure:
fmap :: (a -> b) -> f a -> f b
Examples:
fmap (* 2) (Just 5) // Just 10
fmap (* 2) Nothing // Nothing
fmap (* 2) [1, 2, 3] // [2, 4, 6]
Monad
A Monad extends Functor with two operations:
return :: a -> m a // wrap a value
(>>=) :: m a -> (a -> m b) -> m b // bind / sequence
>>= (pronounced "bind") extracts the value from m a, passes it to the function, and
returns the resulting m b. The monad laws ensure that sequencing is associative and that
return acts as a neutral element.
The >> operator is >>= where the second argument ignores the result:
(>>) :: m a -> m b -> m b
ma >> mb = ma >>= \_ -> mb
The mdo block
mdo is syntactic sugar for a chain of >>= / >> calls. It is the standard way to
sequence monadic operations in SCL:
liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c
liftM2 f x y = mdo
a <- x
b <- y
return (f a b)
Each a <- expr desugars to expr >>= \a -> .... A bare expression (without <-)
desugars to expr >> ....
List as a monad
[] is a monad where >>= means "apply the function to every element and concatenate
the results" (non-determinism / cartesian product):
> mdo
x <- [1, 2, 3]
y <- [10, 20]
return (x + y)
[11, 21, 12, 22, 13, 23]
This is equivalent to a list comprehension [x + y | x <- [1,2,3], y <- [10,20]].
Maybe as a monad
Maybe is a monad where >>= short-circuits to Nothing as soon as any step fails.
This eliminates nested match expressions when chaining fallible operations:
safeHead :: [a] -> Maybe a
safeHead [] = Nothing
safeHead (x:_) = Just x
safeTail :: [a] -> Maybe [a]
safeTail [] = Nothing
safeTail (_:xs) = Just xs
secondElement :: [a] -> Maybe a
secondElement xs = mdo
rest <- safeTail xs
safeHead rest
The edo block
edo is a variant of mdo for effectful (non-pure) monads such as <Proc> or
<ReadGraph>. To use edo, the module header must declare the feature:
module {
features = [edo]
}
With edo, monadic operations interleave with ordinary <Proc> effects:
readAndPrint :: String -> <Proc> ()
readAndPrint path = edo
lines <- readLines path
for lines print
Sequence monad
Sequence is the monad used for discrete-event simulation control (see 3.01 Simulation
sequences). mdo is the recommended syntax for composing sequences:
waitAndAct :: Sequence ()
waitAndAct = mdo
waitCondition (getVar "BUTTON#BINARY_VALUE")
wait 5.0
execute (setVar "VALVE#VA11_POSITION" 0.0)
Key monad combinators
| Function |
Type |
Description |
return |
a -> m a |
Wrap a pure value |
>>= |
m a -> (a -> m b) -> m b |
Bind / sequence with result |
>> |
m a -> m b -> m b |
Sequence, discard first result |
fmap |
(a -> b) -> m a -> m b |
Map over the contained value |
join |
m (m a) -> m a |
Flatten one layer of monad |
sequence |
[m a] -> m [a] |
Run a list of actions, collect results |
replicateM |
Integer -> m a -> m [a] |
Run an action N times, collect results |
repeatForever |
m a -> m b |
Repeat an action indefinitely (sequences only) |
|